/*
 * File     : Utf16Coder.java
 * Created  : 10 Nov 2011
 *
 * Copyright © 2011 Matthew Wilson (mj. {my-surname} .uk {at} gmail.com)
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package com.googlecode.dni.internal.string;

import static com.googlecode.dni.internal.MemoryAccess.MEMORY_ACCESSOR;

import java.nio.ByteOrder;
import java.nio.charset.Charset;

import com.googlecode.dni.internal.DniTypes;
import com.googlecode.dni.internal.NativeUnsafeAccess;

/**
 * <p>
 *  Provides a faster UTF-16 encoding than that in the default platform.
 * </p>
 *
 * @author Matthew Wilson
 */
public final class Utf16Coder
{

    /**
     * Threshold of string length above which strings are processed in native
     * code.  This value was found empirically.
     */
    private static final int NATIVE_THRESHOLD = 9;


    /** Prevents instantiation. */
    private Utf16Coder()
    {
        throw new UnsupportedOperationException();
    }

    /**
     * Obtains an upper bound on the space required for the given string with a
     * null terminator.
     *
     * @param string
     *            the string
     * @return the size in bytes
     */
    public static int getByteCount( final String string )
    {
        return string.length() * 2 + 2;
    }

    /**
     * Encodes the given string into the given buffer.  The position is then
     * undefined.
     *
     * @param string
     *            the string
     * @param pointer
     *            the pointer
     *
     * @return the number of bytes used (including terminator)
     */
    public static int encode( final String string,
                              final long pointer )
    {
        if ( string.length() > NATIVE_THRESHOLD )
        {
            return NativeUnsafeAccess.writeUtf16String( string, pointer );
        }

        int i;
        for ( i = 0; i < string.length(); i++ )
        {
            MEMORY_ACCESSOR.putChar( pointer + ( i << 1 ), string.charAt( i ) );
        }

        MEMORY_ACCESSOR.putChar( pointer + ( i << 1 ), (char) 0 );

        return string.length() + 2;
    }

    /**
     * Reads a null-terminated string.
     *
     * @param pointer
     *            the start of the string
     *
     * @return a string
     */
    public static String decode( final long pointer )
    {
        if ( pointer == 0 )
        {
            return null;
        }

        // TODO v1.1 attempt it in Java for very short strings

        return NativeUnsafeAccess.readUtf16String( pointer );
    }

    /**
     * {@link FastCharset} implementation for {@link Utf16Coder}.
     *
     * @author Matthew Wilson
     */
    static final class Utf16FastCharset extends FastCharset
    {
        /** Creates a new instance. */
        Utf16FastCharset()
        {
            super( getCharsetName(),
                   DniTypes.INTERNAL_STRING_PACKAGE + "/" + "Utf16Coder",
                   Charset.forName( getCharsetName() ),
                   2 );
        }

        private static String getCharsetName()
        {
            if ( ByteOrder.nativeOrder() == ByteOrder.BIG_ENDIAN )
            {
                return "UTF-16BE";
            }
            return "UTF-16LE";
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public int encode( final String string, final long pointer )
        {
            return Utf16Coder.encode( string, pointer );
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public String decode( final long pointer )
        {
            return Utf16Coder.decode( pointer );
        }
    }

}
